package com.simen.hellobytes.socket;

import com.simen.hellobytes.socket.helper.MessageListener;
import com.simen.hellobytes.socket.helper.SocketStatusListener;
import com.simen.hellobytes.socket.helper.StatusListener;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @author Created by Simen.
 * @date 创建日期 2018/5/25 12:53
 * @modify 修改者 Simen
 */
public class SocketServer extends AbstractSocket {

    public static final int READBACKWITHBYTE = 0;
    public static final int READBACKWITHSTR = 1;

    private static final int EOF = -1;

    private int port;

    private ServerSocket serverSocket = null;

    private Socket socket = null;
    private InputStream inputStream = null;
    private OutputStream outputStream = null;

    private boolean isRunning = false;

    private int readContentType = READBACKWITHBYTE;

    private boolean closeClientWhenMessageListenerNotSet = true;

    private BufferedReader bufferedReader = null;

    private MessageListener messageListener = null;

    private StatusListener statusListener = null;

    private SocketStatusListener socketStatusListener = null;

    public SocketServer(int port) {
        this.port = port;
    }

    public SocketServer(int port, MessageListener<String,Socket> messageListener) {
        this.port = port;
        this.messageListener = messageListener;
    }

    public void listen() {
        isRunning = true;

        try {
            if (statusListener != null) {
                statusListener.onStatus(StatusListener.INIT);
            }

            serverSocket = new ServerSocket(port);

            if (mAcceptTimeout != -1) {
                serverSocket.setSoTimeout(mAcceptTimeout);
            }

            serverSocket.setReuseAddress(reuseAddress);

            if (statusListener != null) {
                statusListener.onStatus(StatusListener.ALIVE);
            }

            while (isRunning
                    && (socket = serverSocket.accept()) != null) {

                int clientFlag = MessageListener.DISCONNECT;
                int read = 0;
                byte[] buf = new byte[bufSize];

                if (socketStatusListener != null) {
                    socketStatusListener.onStatus(SocketStatusListener.INIT, socket);
                }

                try {
                    if (mReadTimeout != -1) {
                        socket.setSoTimeout(mReadTimeout);
                    }

                    if (recSocketBufSize != -1) {
                        socket.setReceiveBufferSize(recSocketBufSize);
                    }

                    if (sendSocketBufSize != -1) {
                        socket.setSendBufferSize(sendSocketBufSize);
                    }

                    if (socketStatusListener != null) {
                        socketStatusListener.onStatus(SocketStatusListener.ALIVE, socket);
                    }

                    if (messageListener != null) {
                        inputStream = socket.getInputStream();
                        outputStream = socket.getOutputStream();

                        if (readContentType == READBACKWITHBYTE) {
                            read = inputStream.read(buf);

                            while (isRunning
                                    && (clientFlag = messageListener.onMessage(buf, read, socket)) == MessageListener.KEEP
                                    && read != EOF) {
                                read = inputStream.read(buf);
                            }
                        } else if (readContentType == READBACKWITHSTR) {
                            //按行读取
                            bufferedReader = new BufferedReader(new InputStreamReader(inputStream));

                            String readLine = bufferedReader.readLine();

                            while (isRunning
                                    && (clientFlag = messageListener.onMessage(readLine,0, socket)) == MessageListener.KEEP
                                    && readLine != null
                                    ) {
                                readLine = bufferedReader.readLine();
                            }
                        }
                    }
                } catch (IOException e) {
                    e.printStackTrace();

                    releaseClient();
                    continue;
                }

                if ((closeClientWhenMessageListenerNotSet && clientFlag == MessageListener.DISCONNECT) || read == EOF) {
                    releaseClient();
                }

            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            closeImmediately();
        }

        isRunning = false;

        if (statusListener != null) {
            try {
                statusListener.onStatus(StatusListener.DEAD);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public void closeImmediately() {
        closeClient();

        if (serverSocket != null) {
            try {
                serverSocket.close();
            } catch (IOException e) {
            }
        }
    }

    private void closeClient() {
        if (outputStream != null) {
            try {
                outputStream.close();
            } catch (IOException e) {
            }
        }
        if (bufferedReader != null) {
            try {
                bufferedReader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else if (inputStream != null) {
            try {
                inputStream.close();
            } catch (IOException e) {
            }
        }
        if (socket != null) {
            try {
                socket.close();
            } catch (IOException e) {
            }
        }
    }

    public void setCloseClientWhenMessageListenerNotSet(boolean closeClientWhenMessageListenerNotSet) {
        this.closeClientWhenMessageListenerNotSet = closeClientWhenMessageListenerNotSet;
    }

    public boolean isCloseClientWhenMessageListenerNotSet() {
        return closeClientWhenMessageListenerNotSet;
    }

    private void releaseClient() throws IOException {
        if (socketStatusListener != null) {
            socketStatusListener.onStatus(SocketStatusListener.WILL_DEAD, socket);
        }

        closeClient();

        if (socketStatusListener != null) {
            socketStatusListener.onStatus(SocketStatusListener.DEAD, socket);
        }
    }

    public void setReadContentType(int readContentType) {
        this.readContentType = readContentType;
    }

    public int getReadContentType() {
        return readContentType;
    }

    public boolean isRunning() {
        return isRunning;
    }

    public void setRunning(boolean running) {
        isRunning = running;
    }

    public void setMessageListener(MessageListener messageListener) {
        this.messageListener = messageListener;
    }

    public MessageListener getMessageListener() {
        return messageListener;
    }

    public StatusListener getStatusListener() {
        return statusListener;
    }

    public void setStatusListener(StatusListener statusListener) {
        this.statusListener = statusListener;
    }

    public SocketStatusListener getSocketStatusListener() {
        return socketStatusListener;
    }

    public void setSocketStatusListener(SocketStatusListener socketStatusListener) {
        this.socketStatusListener = socketStatusListener;
    }
}
